package com.xnyzc.lhy.didi.service.impl.xiecheng;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xnyzc.lhy.common.component.redis.RedisCommon;
import com.xnyzc.lhy.common.constant.RedisPrefixConstant;
import com.xnyzc.lhy.common.entity.GouldResult;
import com.xnyzc.lhy.common.exception.DiDiException;
import com.xnyzc.lhy.common.penum.EDiDiErrorCode;
import com.xnyzc.lhy.common.penum.cms.EVehicleType;
import com.xnyzc.lhy.common.penum.order.EOrderChannelType;
import com.xnyzc.lhy.common.util.CheckUtil;
import com.xnyzc.lhy.common.util.IDUtil;
import com.xnyzc.lhy.didi.mapper.price.OaSysBillRuleMapper;
import com.xnyzc.lhy.didi.mapper.price.OaSysBillSpecialDateRuleMapper;
import com.xnyzc.lhy.didi.mapper.price.OaSysBillSpecialTimeRuleMapper;
import com.xnyzc.lhy.didi.mapper.price.OaSysNewBillRuleMapper;
import com.xnyzc.lhy.didi.service.xiecheng.XcPriceService;
import com.xnyzc.lhy.mis.entity.system.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.*;

/**
 * <p>
 * 路线规划与计费
 * </p>
 *
 * @author rhr
 * @since 2020-11-15
 */
@Service
@Slf4j
public class XcPriceServiceImpl implements XcPriceService {

    @Autowired
    private OaSysBillRuleMapper oaSysBillRuleMapper;

    @Autowired
    private RedisCommon redisCommon;

    @Autowired
    private OaSysBillSpecialTimeRuleMapper oaSysBillSpecialTimeRuleMapper;

    @Autowired
    private OaSysBillSpecialDateRuleMapper oaSysBillSpecialDateRuleMapper;

    @Autowired
    private OaSysNewBillRuleMapper oaSysNewBillRuleMapper;

    @Override
    public HashMap<String, Object> getPriceDetail(Integer channel, String cityCode, EVehicleType vehicleType, Integer callType,
                                                  Date startDate, GouldResult.RouteBean router, RulesParam rulesMap,
                                                  String ruleId, int robable, Double premium, Integer drawRatio) {
        List<HashMap<String, Object>> list = new ArrayList<>();
        int cnt = 0;
        // 查询价格模板
        List<OaSysBillRuleMini> oaSysBillRules = rulesMap.getBillRules();
        if (oaSysBillRules == null || oaSysBillRules.size() == 0) {
            return null;
        }
        // 查询特殊价格模板
        List<SpecialRulesParam> specialTimeRules = rulesMap.getSpecialTimeRules();
        List<SpecialRulesParam> specialDateRules = rulesMap.getSpecialDateRules();
        OaSysBillRuleMini oaSysBillRule = null;
        // 获取所有的路径规划
        List<GouldResult.RouteBean.PathsBean> paths = router.getPaths();

        int size = paths.size();
        for (int i = 0; i < size; i++) {
            GouldResult.RouteBean.PathsBean path = paths.get(i);
            // 获取里程 单位米
            String distance = path.getDistance();
            if (CheckUtil.strIsEmpty(distance)) {
                throw DiDiException.create(EDiDiErrorCode.mileageObtainFail);
            }
            BigDecimal distanceM = new BigDecimal(distance);
            BigDecimal distanceKm = distanceM.divide(new BigDecimal(1000));
            distanceKm = distanceKm.setScale(1, BigDecimal.ROUND_CEILING);
            // 获取行驶时间
            if (CheckUtil.strIsEmpty(path.getDuration())) {
                throw DiDiException.create(EDiDiErrorCode.travelTimeObtainFail);
            }

            Double time = Math.rint(Double.valueOf(path.getDuration()) / 60);
            String mtolls = path.getTolls();
            int tolls = 0;
            if (CheckUtil.strIsNotEmpty(mtolls)) {
                tolls = Integer.valueOf(mtolls);
            }

            for (OaSysBillRuleMini rule : oaSysBillRules) {
                //1.大于等于分段起点；2.小于分段终点
                if ((distanceKm.compareTo(rule.getStartMileage()) == 1 || distanceKm.compareTo(rule.getStartMileage()) == 0) && distanceKm.compareTo(rule.getEndMileage()) == -1) {
                    oaSysBillRule = rule;
                    break;
                }
            }
            if (oaSysBillRule == null) {
                //默认使用最后一个数据-即最大值
                oaSysBillRule = oaSysBillRules.get(oaSysBillRules.size() - 1);
            }
            HashMap<String, Object> priceMapNew = this.ExpectedFee(channel, time, distanceKm.doubleValue(), tolls, startDate,
                    oaSysBillRule, specialTimeRules, specialDateRules, ruleId, premium, drawRatio);
            priceMapNew.put("distanceKm", distanceKm);
            priceMapNew.put("distanceM", distanceM);
            priceMapNew.put("duration", path.getDuration());
            list.add(priceMapNew);
            if (list.size() > 1) {
                HashMap<String, Object> priceMapOld = list.get(cnt);
                BigDecimal totalFeeOld = (BigDecimal) priceMapOld.get("totalFee");
                BigDecimal totalFeeNew = (BigDecimal) priceMapNew.get("totalFee");
                // 获取比较价格最低
                if (totalFeeNew.compareTo(totalFeeOld) < 0) {
                    cnt = i;
                }
            }
        }
        HashMap<String, Object> priceMap = list.get(cnt);
        // 获取选择的规划路径
        GouldResult.RouteBean.PathsBean pathsBean = paths.get(cnt);
        String estDistance = pathsBean.getDistance();  //得到预估距离
        // 保存预估点位
        List<GouldResult.RouteBean.PathsBean.StepsBean> steps = pathsBean.getSteps();
        StringBuffer stringBuffer = new StringBuffer();
        for (GouldResult.RouteBean.PathsBean.StepsBean stepsBean : steps) {
            stringBuffer.append(stepsBean.getPolyline());
            stringBuffer.append(";");
        }

        stringBuffer.deleteCharAt(stringBuffer.length() - 1);
        //priceMap.put(EGould.gouldPolyline.getValue(), stringBuffer.toString());//预估路线
        priceMap.put("robable", robable);
        priceMap.put("estDistance", estDistance);
        return priceMap;
    }

    @Deprecated
    private HashMap<String, Object> selectNewBillRules(Integer channel, String cityCode, Integer vehType, Integer callType) {
//        //查询redis是否存在计价规则 TODO 注意车型
        HashMap<String, Object> reMap = new HashMap<>();
        List<NewBillRules> billRules = null;

        String key = RedisPrefixConstant.BILL_RULES + "_" + channel + "_" + cityCode + "_" + callType;
        String oaSysBillRulesJson = redisCommon.getStr(key);
        if (CheckUtil.strIsNotEmpty(oaSysBillRulesJson)) {
            billRules = JSON.parseArray(oaSysBillRulesJson, NewBillRules.class);
        }
        //如果计价规则为空,继续向数据库寻找
        if (billRules == null || billRules.size() == 0) {
            QueryWrapper<NewBillRules> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq(NewBillRules.CHANNEL, channel);
            queryWrapper.eq(NewBillRules.AREA_ID, Integer.parseInt(cityCode));
            queryWrapper.eq(NewBillRules.CALL_TYPE, callType);
            queryWrapper.eq(NewBillRules.DELETED, 0);
            // 查询普通时间
            billRules = oaSysNewBillRuleMapper.selectList(queryWrapper);
            reMap = getData(billRules, vehType);
            redisCommon.set(key, JSON.toJSONString(billRules));
            return reMap;
        }
        reMap = getData(billRules, vehType);
        return reMap;
    }

    private HashMap<String, Object> getData(List<NewBillRules> billRules, Integer vehType) {
        HashMap<String, Object> reMap = new HashMap<>(16);
        List<OaSysBillRuleMini> oaSysNewBillRules = new ArrayList<>();
        List<SpecialRulesParam> specialDateRules = new ArrayList<>();
        List<SpecialRulesParam> specialTimeRules = new ArrayList<>();
        for (NewBillRules newBillRule : billRules) {
            List<RulesParam> fees = JSON.parseArray(newBillRule.getJsonRules(), RulesParam.class);
            for (RulesParam r : fees) {
                if (r.getVehicleType().equals(vehType)) {
                    oaSysNewBillRules.addAll(r.getBillRules());
                    reMap.put("billRule", oaSysNewBillRules);
                    specialDateRules.addAll(r.getSpecialDateRules());
                    reMap.put("sData", specialDateRules);
                    specialTimeRules.addAll(r.getSpecialTimeRules());
                    reMap.put("sTime", specialTimeRules);
                    break;
                }
            }
        }
        return reMap;
    }

    private List<OaSysBillRule> selectBillRules(Integer channel, String cityCode, Integer vehType, Integer callType) {
//        //查询redis是否存在计价规则
        List<OaSysBillRule> oaSysNewBillRules = null;
        String key = RedisPrefixConstant.BILL_RULES + "_" + channel + "_" + cityCode + "_" + vehType + "_" + callType;
        String oaSysBillRulesJson = redisCommon.getStr(key);
        if (CheckUtil.strIsNotEmpty(oaSysBillRulesJson)) {
            oaSysNewBillRules = JSON.parseArray(oaSysBillRulesJson, OaSysBillRule.class);
        }
        //如果计价规则为空,继续向数据库寻找
        if (oaSysNewBillRules == null || oaSysNewBillRules.size() == 0) {
            QueryWrapper<OaSysBillRule> areaQueryWrapper = new QueryWrapper<>();
            areaQueryWrapper.eq(OaSysBillRule.CHANNEL_ID, channel);
            areaQueryWrapper.eq(OaSysBillRule.CITY_ID, Integer.parseInt(cityCode));
            areaQueryWrapper.eq(OaSysBillRule.VEHICLE_TYPE, vehType);
            areaQueryWrapper.eq(OaSysBillRule.CALL_TYPE, callType);
            areaQueryWrapper.eq(OaSysBillRule.DELETED, 0);
            // 查询普通时间
            oaSysNewBillRules = oaSysBillRuleMapper.selectList(areaQueryWrapper);
            redisCommon.set(key, JSON.toJSONString(oaSysNewBillRules));
        }
        return oaSysNewBillRules;
    }

    public List<OaSysBillSpecialTimeRule> selectBillSpecialTimeRules(Integer channel, String cityCode, Integer vehType, Integer callType) {
        List<OaSysBillSpecialTimeRule> specialTimeRules = new ArrayList<>();
        //从redis中获取特殊时段计费规则
        String key = RedisPrefixConstant.BILL_SPECIAL_TIME_RULES + "_" + channel + "_" + cityCode + "_" + vehType + "_" + callType;
        String rulesJson = redisCommon.getStr(key);
        //特殊时间规则
        if (CheckUtil.strIsNotEmpty(rulesJson)) {
            specialTimeRules = JSON.parseArray(rulesJson, OaSysBillSpecialTimeRule.class);
        }
        //如果为空继续向数据库获取
        if (specialTimeRules == null || specialTimeRules.size() <= 0) {

            QueryWrapper<OaSysBillSpecialTimeRule> areaQueryWrapper = new QueryWrapper<>();
            areaQueryWrapper.eq(OaSysBillSpecialTimeRule.CHANNEL_ID, channel);
            areaQueryWrapper.eq(OaSysBillSpecialTimeRule.CITY_ID, Integer.parseInt(cityCode));
            // 目前先规定一个公司ID
            areaQueryWrapper.eq(OaSysBillSpecialTimeRule.VEHICLE_TYPE, vehType);
            areaQueryWrapper.eq(OaSysBillSpecialTimeRule.CALL_TYPE, callType);
            areaQueryWrapper.eq(OaSysBillSpecialTimeRule.DELETED, 0);
            specialTimeRules = oaSysBillSpecialTimeRuleMapper.selectList(areaQueryWrapper);
            redisCommon.set(key, JSON.toJSONString(specialTimeRules));
        }
        return specialTimeRules;
    }

    public List<OaSysBillSpecialDateRule> selectBillSpecialDateRules(Integer channel, String cityCode, Integer vehType, Integer callType) {
        List<OaSysBillSpecialDateRule> specialDateRules = new ArrayList<>();
        //从redis中获取特殊日期计费规则
        String key = RedisPrefixConstant.BILL_SPECIAL_DATE_RULES + "_" + channel + "_" + cityCode + "_" + vehType + "_" + callType;
        String rulesJson = redisCommon.getStr(key);
        //特殊日期规则
        if (CheckUtil.strIsNotEmpty(rulesJson)) {
            specialDateRules = JSON.parseArray(rulesJson, OaSysBillSpecialDateRule.class);
        }
        //如果为空继续向数据库获取
        if (null == specialDateRules || specialDateRules.size() <= 0) {
            QueryWrapper<OaSysBillSpecialDateRule> areaQueryWrapper = new QueryWrapper<>();
            areaQueryWrapper.eq(OaSysBillSpecialDateRule.CHANNEL_ID, channel);
            areaQueryWrapper.eq(OaSysBillSpecialDateRule.CITY_ID, Integer.parseInt(cityCode));
            // 目前先规定一个公司ID
            areaQueryWrapper.eq(OaSysBillSpecialDateRule.VEHICLE_TYPE, vehType);
            areaQueryWrapper.eq(OaSysBillSpecialDateRule.CALL_TYPE, callType);
            areaQueryWrapper.eq(OaSysBillSpecialDateRule.DELETED, 0);

            specialDateRules = oaSysBillSpecialDateRuleMapper.selectList(areaQueryWrapper);
            redisCommon.set(key, JSON.toJSONString(specialDateRules));
        }
        return specialDateRules;
    }

    /**
     * 预估价金额
     * 做不到十分准确,只能说预估大约
     *
     * @return
     */

    public HashMap<String, Object> ExpectedFee(Integer channel, double time, double mileage, int tolls, Date tripStartTime,
                                               OaSysBillRuleMini oaSysBillRule, List<SpecialRulesParam> specialTimeRules,
                                               List<SpecialRulesParam> specialDateRules, String ruleId, Double premium, Integer drawRatio) {
        //行驶时长
        BigDecimal timeBig = BigDecimal.valueOf(time);
        //开始时间
        long startTime = tripStartTime.getTime();
        //1.算基础价格
        //起步价
        BigDecimal startFee = new BigDecimal(String.valueOf(oaSysBillRule.getStartFee()));
        //里程费
        BigDecimal mileageFee = new BigDecimal(0);
        //处理相减小于0，防止运算为负值
        if (mileage - oaSysBillRule.getStartingMileage().doubleValue() > 0) {
            mileageFee = mileageFee.add(new BigDecimal((mileage - oaSysBillRule.getStartingMileage().doubleValue()) * oaSysBillRule.getMileageFee().doubleValue()));
        }
        //其他费用
        BigDecimal otherFee = new BigDecimal(String.valueOf(oaSysBillRule.getOtherFee()));
        BigDecimal percent = new BigDecimal(100);
        //2.算特殊日期价格
        BigDecimal radioDate = new BigDecimal(0.00);
        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            if (CheckUtil.objIsNotEmpty(specialDateRules)) {
                for (SpecialRulesParam specialDateRule : specialDateRules) {
                    if (radioDate.compareTo(BigDecimal.ZERO) == 1) {
                        break;
                    }
                    Date specialStart = new Date();
                    Date specialEnd = new Date();
                    try {
                        specialStart = sdfDate.parse(specialDateRule.getTimeKey().split(",")[0]);
                        specialEnd = sdfDate.parse(specialDateRule.getTimeKey().split(",")[1]);
                    } catch (Exception e) {
                        log.error("开始或结束时间格式异常", e);
                    }
                    //判断是否在时间区间
                    if (startTime >= specialStart.getTime() && startTime < specialEnd.getTime()) {
                        //起始时间在特殊日期内才算
                        for (BillSpecialRule specialRule : specialDateRule.getOaSysBillSpecialRuleList()) {
                            //计算里程命中区间 里程大于等于起始公里并且小于结束公里
                            if ((BigDecimal.valueOf(mileage).compareTo(specialRule.getStartMileage()) == 1 || BigDecimal.valueOf(mileage).compareTo(specialRule.getStartMileage()) == 0)
                                    && BigDecimal.valueOf(mileage).compareTo(specialRule.getEndMileage()) == -1) {
                                //加价比例
                                radioDate = new BigDecimal(specialRule.getProportion());
                                break;
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("处理特殊日期加价异常", e);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
        //3.算特殊时间段价格
        BigDecimal radioTime = new BigDecimal(0.00);
        try {
            Calendar startCalendar = Calendar.getInstance();
            //tripStartTime  乘客出发时间
            startCalendar.setTime(tripStartTime);
            int appointTime = startCalendar.get(Calendar.HOUR_OF_DAY) * 60 + startCalendar.get(Calendar.MINUTE);
            if (CheckUtil.objIsNotEmpty(specialTimeRules)) {
                for (SpecialRulesParam specialTimeRule : specialTimeRules) {
                    if (radioTime.compareTo(BigDecimal.ZERO) == 1) {
                        break;
                    }
                    Calendar specialStartTime = Calendar.getInstance();
                    specialStartTime.setTime(sdf.parse(specialTimeRule.getTimeKey().split(",")[0]));
                    int specialStart = specialStartTime.get(Calendar.HOUR_OF_DAY) * 60 + specialStartTime.get(Calendar.MINUTE);
                    Calendar specialEndTime = Calendar.getInstance();
                    specialEndTime.setTime(sdf.parse(specialTimeRule.getTimeKey().split(",")[1]));
                    int specialEnd = specialEndTime.get(Calendar.HOUR_OF_DAY) * 60 + specialEndTime.get(Calendar.MINUTE);
                    if (appointTime >= specialStart && appointTime <= specialEnd) {
                        //起始时间在特殊时间段内才算
                        for (BillSpecialRule specialRule : specialTimeRule.getOaSysBillSpecialRuleList()) {
                            //计算里程命中区间 里程大于等于起始公里并且小于结束公里
                            if ((BigDecimal.valueOf(mileage).compareTo(specialRule.getStartMileage()) == 1 || BigDecimal.valueOf(mileage).compareTo(specialRule.getStartMileage()) == 0)
                                    && BigDecimal.valueOf(mileage).compareTo(specialRule.getEndMileage()) == -1) {
                                //加价比例
                                radioTime = new BigDecimal(specialRule.getProportion());
                                break;
                            }
                        }
                    }
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        //高速路费
        BigDecimal tollsFee = new BigDecimal(tolls);
        HashMap<String, Object> priceMap = new HashMap<>();
        priceMap.put("ruleId", ruleId);
        priceMap.put("estimateId", IDUtil.nextId());
        priceMap.put("timeCount", time);
        BigDecimal totalFee = new BigDecimal(0);
        //总费用=起步价+里程费+其他费用
        totalFee = totalFee.add(startFee).add(mileageFee).add(otherFee);
        //计算特殊日期和特殊时间的加价比例总和
        totalFee = totalFee.add(totalFee.multiply(radioDate.add(radioTime)).divide(BigDecimal.valueOf(100), BigDecimal.ROUND_HALF_UP));
        //携程不需要高速费
        totalFee = channel != EOrderChannelType.xiecheng.getType() ? totalFee.add(tollsFee) : totalFee;
        //保险费
        //保险费
        BigDecimal permiumFee = new BigDecimal(premium);
        totalFee = totalFee.setScale(0, BigDecimal.ROUND_UP).setScale(2, RoundingMode.HALF_UP);
        //高速路费和保险费算抽成，所以需要在计算完抽成后添加
        totalFee = totalFee.add(permiumFee);
        BigDecimal drawFee = totalFee.subtract(totalFee.multiply(new BigDecimal(drawRatio)).
                divide(percent, 2, RoundingMode.HALF_UP)).subtract(permiumFee);
        //处理全部费用以元下的单位，四舍五入向上取整
        priceMap.put("totalFee", totalFee);
        priceMap.put("startFee", startFee.setScale(0, BigDecimal.ROUND_UP).setScale(2, RoundingMode.HALF_UP));
        priceMap.put("mileageFee", mileageFee.setScale(0, BigDecimal.ROUND_UP).setScale(2, RoundingMode.HALF_UP));
        priceMap.put("otherFee", otherFee.setScale(0, BigDecimal.ROUND_UP).setScale(2, RoundingMode.HALF_UP));
        priceMap.put("tollsFee", tollsFee.setScale(0, BigDecimal.ROUND_UP).setScale(2, RoundingMode.HALF_UP));
        priceMap.put("permiumFee", permiumFee.setScale(0, BigDecimal.ROUND_UP).setScale(2, RoundingMode.HALF_UP));
        priceMap.put("drawFee", drawFee.setScale(2, BigDecimal.ROUND_UP).setScale(2, RoundingMode.HALF_UP));
        return priceMap;
    }

    public static void main(String[] args) {
        BigDecimal totalFee=new BigDecimal(75);
        totalFee = totalFee.setScale(0, BigDecimal.ROUND_UP).setScale(2, RoundingMode.HALF_UP);
        System.out.println(totalFee);
    }

}
